package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.design.*;
import com.example.supplychainbackend.controller.dto.sales.*;
import com.example.supplychainbackend.controller.dto.user.UserSigDto;
import com.example.supplychainbackend.dao.design.BOMDao;
import com.example.supplychainbackend.dao.design.ECNDao;
import com.example.supplychainbackend.entity.design.BOMEntity;
import com.example.supplychainbackend.entity.design.ECNEntity;
import com.example.supplychainbackend.mapper.DesignMapper;
import com.example.supplychainbackend.service.BlockchainService;
import com.example.supplychainbackend.service.DesignService;
import com.example.supplychainbackend.service.SalesService;
import com.example.supplychainbackend.tool.DateTimeUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class DesignServiceImpl implements DesignService {
    private final SalesService salesService;

    private final DesignMapper mapper;

    private final BOMDao bomDao;
    private final ECNDao ecnDao;
    private final BlockchainService blockchainService;

    @Override
    public BOMDto getBOM(String lineNumber) {
        BOMEntity entity = bomDao.findByBomLineNumber(lineNumber).get();
        return mapper.toBOMDto(entity);
    }

    @Override
    public BOMDto getBOMByCode(String materialCode) {
        BOMEntity entity = bomDao.findByMaterialCode(materialCode);
        if (entity == null) throw new RuntimeException("bom " + materialCode + " not found");
        return mapper.toBOMDto(entity);
    }

    @Override
    public BOMDto updateBOM(String materialCode, BOMDto form) {
        BOMEntity entity = mapper.toBOMEntity(form);
        entity.setMaterialCode(materialCode);

        UserSigDto sig = UserSigDto.builder().
                userName(form.getUserName()).userId(form.getUserId())
                .enterpriseId(form.getEnterpriseId()).enterpriseName(form.getEnterpriseName()).build();

        ECNDto ecnDto = compareBOMChange(getBOMByCode(materialCode), form, sig);
        createECN(ecnDto);

        return mapper.toBOMDto(bomDao.save(entity));
    }

    @Override
    public String createBOM(BOMDto form) {
        BOMEntity entity = mapper.toBOMEntity(form);

        UserSigDto sig = UserSigDto.builder().
                userName(form.getUserName()).userId(form.getUserId())
                .enterpriseId(form.getEnterpriseId()).enterpriseName(form.getEnterpriseName()).build();

        ECNDto ecnDto = compareBOMChange(null, form, sig);
        createECN(ecnDto);
        entity.setOnChain(false);

        return bomDao.save(entity).getMaterialCode();
    }

    @Override
    public List<BOMDto> getBOMsByOrder(String oid) {
        List<BOMEntity> bomEntities = bomDao.findAllByOrderId(oid);
        return mapper.toBOMDtoList(bomEntities);
    }

    @Override
    public List<BOMDto> getAllBOMs() {
        List<BOMEntity> bomEntities = bomDao.findAll();
        return mapper.toBOMDtoList(bomEntities);
    }

    @Override
    public Long deleteBOM(String materialCode, UserSigDto userSigDto) {
        ECNDto ecnDto = compareBOMChange(getBOMByCode(materialCode), null, userSigDto);
        ecnDto.setUserName(userSigDto.getUserName());
        ecnDto.setUserId(userSigDto.getUserId());
        ecnDto.setEnterpriseName(userSigDto.getEnterpriseName());
        ecnDto.setEnterpriseId(userSigDto.getEnterpriseId());
        ecnDto.setModifier(ecnDto.getUserName());
        createECN(ecnDto);
        return bomDao.deleteByMaterialCode(materialCode);
    }

    @Override
    public ECNDto getECN(String ecnNumber) {
        ECNEntity entity = ecnDao.findByEcnNumber(ecnNumber).get();
        return mapper.toECNDto(entity);
    }

    @Override
    public ECNDto updateECN(String ecnNumber, ECNDto form) {
        if (!ecnDao.findByEcnNumber(ecnNumber).isPresent()) {
            throw new RuntimeException("ecn "+ ecnNumber+ " not exists");
        }
        ECNEntity entity = mapper.toECNEntity(form);
        entity.setEcnNumber(ecnNumber);
        return mapper.toECNDto(ecnDao.save(entity));
    }

    @Override
    public String createECN(ECNDto form) {
        if (form.getEcnNumber()!=null) {
            throw new RuntimeException("manual id assignation not allowed");
        }
        ECNEntity entity = mapper.toECNEntity(form);
        entity.setOnChain(false);
        return ecnDao.save(entity).getEcnNumber();
    }

    @Override
    public List<ECNDto> getAllECNs() {
        List<ECNEntity> ecnEntities = ecnDao.findAll();
        return mapper.toECNDtoList(ecnEntities);
    }

    @Override
    public List<ECNDto> getAllECNsByEid(String eid) {
        List<ECNEntity> ecnEntities = ecnDao.findAllByEnterpriseId(eid);
        return mapper.toECNDtoList(ecnEntities);
    }

    @Override
    public Long deleteECN(String ecnNumber) {
        return ecnDao.deleteByEcnNumber(ecnNumber);
    }

    @Override
    public UploadInfo uploadToChainBOM(String id) {
        return null;
    }

    @Override
    public UploadInfo uploadToChainECN(String id) {
        ECNDto ecn = this.getECN(id);
        if (null!=ecn){
            UploadInfo txInfo = blockchainService.uploadDesignECN(id, ecn.toString());
            ecn.setOnChain(true);
            ecn.setCommitTime(txInfo.getCommitTime());
            ecn.setTransactionId(txInfo.getTransactionId());
            ecnDao.save(mapper.toECNEntity(ecn));
            return txInfo;
        }
        throw new RuntimeException("ECN not found");
    }

    @Override
    public void uploadToChainBOMsByOrder(String id) {
        List<BOMEntity> bomEntities = bomDao.findAllByOrderId(id);
        for (BOMEntity bom:
                bomEntities) {
            UploadInfo txInfo = blockchainService.uploadDesignBOM(bom.getMaterialCode(),bom.toString());
            bom.setOnChain(true);
            bom.setCommitTime(txInfo.getCommitTime());
            bom.setTransactionId(txInfo.getTransactionId());
            bomDao.save(bom);
        }
    }

    @Override
    public void createBOMnoECN(BOMDto parentBom) {

        BOMEntity entity = mapper.toBOMEntity(parentBom);

        bomDao.save(entity);

    }


    public ECNDto compareBOMChange(BOMDto oldBOM, BOMDto newBOM, UserSigDto sigDto){
        ECNDto.ECNDtoBuilder builder = ECNDto.builder();
        BOMDto metaSrc = oldBOM;
        int type = 0;// update
        if (oldBOM == null) {
            if (newBOM == null){
                throw new RuntimeException("compare between 2 empty bom object");
            }
            metaSrc = newBOM;
            type = 1;// add
        }else if (newBOM == null){
            type = 2;// delete
        }
        String orderId = metaSrc.getOrderId();
        OrderDetailDto order = salesService.getOrder(metaSrc.getOrderId());
        ContractDto contract = salesService.getContract(order.getContractNumber());
        builder = builder.salesOrderNumber(String.valueOf(orderId)).salesOrderName(order.getSalesOrderName())
                .contractNumber(contract.getContractNumber()).contractName(contract.getContractName())
                .modifyTime(DateTimeUtils.getCurrentDateTime())
                .modifier(sigDto.getUserName())
                .userName(sigDto.getUserName())
                .userId(sigDto.getUserId())
                .enterpriseName(sigDto.getEnterpriseName())
                .enterpriseId(sigDto.getEnterpriseId())
                .parentMaterial(metaSrc.getMaterialCode()).parentMaterialName(metaSrc.getMaterialName());
        ECNDto ecnDto = builder.build();

        List<ECNDetailDto> detailDtos= new ArrayList<>();
        ECNDetailDto.ECNDetailDtoBuilder detailBuilder = ECNDetailDto.builder();
        switch (type){
            case 0:
                detailBuilder = detailBuilder.originalQuantity(metaSrc.getQuantity()).newQuantity(newBOM.getQuantity())
                        .changeQuantity(newBOM.getQuantity() - metaSrc.getQuantity());
                break;
            case 1:
                detailBuilder = detailBuilder.originalQuantity(0).newQuantity(newBOM.getQuantity())
                        .changeQuantity(newBOM.getQuantity());
                break;
            case 2:
                detailBuilder = detailBuilder.originalQuantity(metaSrc.getQuantity()).newQuantity(0)
                        .changeQuantity(-metaSrc.getQuantity());
                break;
        }
        ECNDetailDto detailDto = detailBuilder.unit(metaSrc.getUnit()).parentMaterial(ecnDto.getParentMaterial())
                .parentMaterialName(ecnDto.getParentMaterialName())
                .build();
        detailDtos.add(detailDto);
        ecnDto.setBomChangeDetail(detailDtos);

        List<ECNRelatedDto> relatedDtos = new ArrayList<>();
        ECNRelatedDto relatedDto1 = ECNRelatedDto.builder().materialNumberBom(metaSrc.getMaterialCode())
                .materialDesc(metaSrc.getMaterialDesc())
                .documentType("SO").documentNumber(String.valueOf(orderId)).contractNumber(contract.getContractNumber())
                .salesOrderName(order.getSalesOrderName())
                .contractName(contract.getContractName()).build();
        switch (type){
            case 0:
                relatedDto1.setEffectType("更新");
                break;
            case 1:
                relatedDto1.setEffectType("增加");
                break;
            case 2:
                relatedDto1.setEffectType("删除");
                break;
        }
        relatedDtos.add(relatedDto1);
        ecnDto.setChangeRelated(relatedDtos);
        return ecnDto;
    }
}